#include "OSS_Upload.h"
#include "tests/cefclient/browser/protocol.h"
#include <conio.h> 
#include <sys/stat.h>
#include "upload_service.h"

static const char * OSS_TYPE_MP3 = "audio/mp3";
static const char * OSS_TYPE_JPEG = "image/jpeg";
static const char * OSS_TYPE_MP4 = "video/mpeg4";   //.mp4
static const char * OSS_TYPE_AVI = "video/avi";     //.avi
static const char * OSS_TYPE_UNKNOWN = "application/octet-stream";     //.object

static const char * OSS_TYPE_TXT = "text/plain";
static const char * OSS_TYPE_BINARY = "application/octet-stream";

static const char OSS_ENDPOINT[] = "oss-cn-shanghai.aliyuncs.com";
static const char ACCESS_KEY_ID[] = "LTAIPapGv0jON3YL";
static const char ACCESS_KEY_SECRET[] = "j4k5xIKFwMxm9Nlt9pi17vXWj3HyUv";
static const char BUCKET_NAME[] = "upupoo-front-test";
static const char TEST_OBJDIR[] = "theme/";

//static const char OSS_NEXT_APPEND_POSITION1[] = "x-oss-next-append-position";
//static const char OSS_OBJECT_TYPE_APPENDABLE1[] = "Appendable";

int  g_progress = 0;


std::wstring UTF82WCS(const char* szU8)
{
	 
	int wcsLen = ::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), NULL, 0);

	 
	wchar_t* wszString = new wchar_t[wcsLen + 1];

	 
	::MultiByteToWideChar(CP_UTF8, NULL, szU8, strlen(szU8), wszString, wcsLen);

	 
	wszString[wcsLen] = '\0';

	std::wstring unicodeString(wszString);

	delete[] wszString;
	wszString = NULL;

	return unicodeString;
}

#define FILE_UPLOAD_SIZE L"uploadsize.ini"
#define SECTION_UPLOAD L"upload"
#define KEY_UPLOAD L"uploadsize"
std::map<void*, std::wstring> OSS_Upload::idmap_;
std::wstring OSS_Upload::id_;
aos_pool_t* OSS_Upload::p_ = nullptr;
bool OSS_Upload::stop_ = false;
std::wstring OSS_Upload::settingpath_;


OSS_Upload::OSS_Upload(std::string  localpaperpath, std::string  paperid, int themetype, std::string  src)
{
	this->local_paper_path_ = localpaperpath; 
	this->themetype_ = themetype;
	this->src_ = src;
	this->paperid_ = paperid;
	id_ = UTF82WCS(paperid.c_str());
	stop_ = false;
	settingpath_ = UTF82WCS(localpaperpath.c_str()) + L"\\" + FILE_UPLOAD_SIZE;
	//idmap_.insert(make_pair(this, id_));
}

OSS_Upload::~OSS_Upload()
{ 
	if (LOGDEBUG) _cprintf(":~OSS_Upload->\n");
	//aos_http_io_deinitialize();
	//if (LOGDEBUG) _cprintf(":~OSS_Upload<-\n");
}

void OSS_Upload::init() {
	// initialize http io system, call it olny once
	if (aos_http_io_initialize(NULL, 0) != AOSE_OK) {
		if (LOGDEBUG) _cprintf("oss initialize failed\n");
		return;
	}
	if (LOGDEBUG) _cprintf("aos initalize done\n");
}

void OSS_Upload::release() {
	aos_http_io_deinitialize();
}

int OSS_Upload::UploadFile() {
	if (!UploadPreview()) return -1;

	if (!UploadUpup())  return -1;

	return UploadData();
}

bool OSS_Upload::UploadPreview()
{ 
	aos_pool_create(&pool_, NULL);

	aos_pool_t *p = NULL;
	aos_string_t bucket;
	aos_string_t object;
	int is_cname = 0;
	aos_table_t *headers = NULL;
	aos_table_t *resp_headers = NULL;
	oss_request_options_t *options = NULL; 
	aos_status_t *s = NULL;
	aos_string_t file;
	aos_list_t resp_body; 

	aos_pool_create(&p, NULL);
	options = oss_request_options_create(p);
	init_sample_request_options(options, is_cname);
	headers = aos_table_make(options->pool, 1); 
	apr_table_set(headers, OSS_CONTENT_TYPE, OSS_TYPE_JPEG);
	aos_str_set(&bucket, BUCKET_NAME);

	//"F:\\downloaded\\Charlie.mp3", "test/101/Charlie.mp3", "audio/mp3"

	std::string remoteobject(TEST_OBJDIR);
	remoteobject.append(paperid_);
	remoteobject.append("/preview.jpg");
	aos_str_set(&object, remoteobject.c_str());
	std::string paperpath(local_paper_path_);
	paperpath.append("\\").append("preview.jpg");;
	aos_str_set(&file, paperpath.c_str());
	aos_list_init(&resp_body);

	s = oss_put_object_from_file(options, &bucket, &object, &file,
		headers, &resp_headers);

	_cprintf("upload %s to %s\n", paperpath.c_str(), remoteobject.c_str());
	//s = oss_do_put_object_from_file(options, &bucket, &object, &file, NULL, NULL, percentage, &resp_headers, &resp_body);

	if (aos_status_is_ok(s)) {
		if (LOGDEBUG) _cprintf("put preview object from file succeeded\n");	 
	}
	else {
		if (LOGDEBUG) _cprintf("put object from file failed, code:%d, error_code : %s, error_msg : %s, request_id : %s\n",
			s->code, s->error_code, s->error_msg, s->req_id); 
		return false;
	}

	aos_pool_destroy(p);
	if (LOGDEBUG) _cprintf("upload done\n");
	return true;
}

bool OSS_Upload::UploadUpup()
{
	aos_pool_create(&pool_, NULL);

	aos_pool_t *p = NULL;
	aos_string_t bucket;
	aos_string_t object;
	int is_cname = 0;
	aos_table_t *headers = NULL;
	aos_table_t *resp_headers = NULL;
	oss_request_options_t *options = NULL;
	aos_status_t *s = NULL;
	aos_string_t file;
	aos_list_t resp_body;

	aos_pool_create(&p, NULL);
	options = oss_request_options_create(p);
	init_sample_request_options(options, is_cname);
	headers = aos_table_make(options->pool, 1);
	apr_table_set(headers, OSS_CONTENT_TYPE, OSS_TYPE_TXT);
	aos_str_set(&bucket, BUCKET_NAME);
	 
	std::string remoteobject(TEST_OBJDIR);
	remoteobject.append(paperid_);
	remoteobject.append("/theme.upup");
	aos_str_set(&object, remoteobject.c_str());
	std::string paperpath(local_paper_path_);
	paperpath.append("\\").append("theme.upup");;
	aos_str_set(&file, paperpath.c_str());
	aos_list_init(&resp_body);

	s = oss_put_object_from_file(options, &bucket, &object, &file,
		headers, &resp_headers);

	_cprintf("upload %s to %s\n", paperpath.c_str(), remoteobject.c_str());
	//s = oss_do_put_object_from_file(options, &bucket, &object, &file, NULL, NULL, percentage, &resp_headers, &resp_body);

	if (aos_status_is_ok(s)) {
		if (LOGDEBUG) _cprintf("put upup object from file succeeded\n");
	}
	else {
		if (LOGDEBUG) _cprintf("put object from file failed, code:%d, error_code : %s, error_msg : %s, request_id : %s\n",
			s->code, s->error_code, s->error_msg, s->req_id);
		return false;
	}

	aos_pool_destroy(p);
	if (LOGDEBUG) _cprintf("upload theme.upup done\n");
	return true;
}


int OSS_Upload::UploadData()
{
	aos_pool_create(&pool_, NULL);
	aos_pool_t *p = NULL;
	aos_string_t bucket;
	aos_string_t object;
	int is_cname = 0;
	aos_table_t *headers = NULL;
	aos_table_t *resp_headers = NULL;
	oss_request_options_t *options = NULL;
	aos_status_t *s = NULL;
	aos_string_t file;
	aos_list_t resp_body;
	aos_table_t *headers1 = NULL;
	aos_table_t *headers2 = NULL;
	//int64_t position = 0;
	//char *next_append_position = NULL;
	//char *object_type = NULL;
	g_progress = 0;
	aos_pool_create(&p, NULL);
	options = oss_request_options_create(p);
	p_ = p;
	init_sample_request_options(options, is_cname);
	headers = aos_table_make(options->pool, 1);
	headers1 = aos_table_make(options->pool, 0);
	headers2 = aos_table_make(options->pool, 0);
	//apr_table_set(headers, OSS_CONTENT_TYPE, OSS_TYPE_TXT);
	aos_str_set(&bucket, BUCKET_NAME);

	std::string remoteobject(TEST_OBJDIR);
	remoteobject.append(paperid_);
	std::string paperpath(local_paper_path_);

	if (themetype_ == 1) {
		remoteobject.append("/").append(src_);		 
		paperpath.append("\\").append(src_);
		if (src_.find(".mp4") > 0) {
			apr_table_set(headers, OSS_TYPE_UNKNOWN, OSS_TYPE_MP4);
		}
		else if (src_.find(".avi") > 0) {
			apr_table_set(headers, OSS_TYPE_UNKNOWN, OSS_TYPE_AVI);
		}
		else {
			apr_table_set(headers, OSS_TYPE_UNKNOWN, OSS_TYPE_UNKNOWN);
		}
	}
	else {
		remoteobject.append("/").append("paper.zip");
		paperpath.append("\\").append("paper.zip");
		apr_table_set(headers, OSS_TYPE_UNKNOWN, OSS_TYPE_TXT);
	}
	 
	aos_str_set(&object, remoteobject.c_str());
	aos_str_set(&file, paperpath.c_str());
	aos_list_init(&resp_body);

	//s = oss_put_object_from_file(options, &bucket, &object, &file,
	//	headers, &resp_headers);

	_cprintf("upload %s to %s\n", paperpath.c_str(), remoteobject.c_str());
	s = oss_do_put_object_from_file(options, &bucket, &object, &file, NULL, NULL, percentage, &resp_headers, &resp_body);

	if (stop_) {
		return 1;
	}

	if (aos_status_is_ok(s)) {
		if (LOGDEBUG) _cprintf("put daat object from file succeeded\n");
	}
	else {
		if (LOGDEBUG) _cprintf("put object from file failed, code:%d, error_code : %s, error_msg : %s, request_id : %s\n",
			s->code, s->error_code, s->error_msg, s->req_id);
		return -1;
	}

	aos_pool_destroy(p);
	if (LOGDEBUG) _cprintf("upload data done\n");
	return 0;
}

void OSS_Upload::init_sample_request_options(oss_request_options_t *options, int is_cname) {
	options->config = oss_config_create(options->pool);
	aos_str_set(&options->config->endpoint, OSS_ENDPOINT);
	aos_str_set(&options->config->access_key_id, ACCESS_KEY_ID);
	aos_str_set(&options->config->access_key_secret, ACCESS_KEY_SECRET);
	options->config->is_cname = is_cname;

	options->ctl = aos_http_controller_create(options->pool, 0);

}


void   OSS_Upload::percentage(int64_t consumed_bytes, int64_t total_bytes)
{
	assert(total_bytes >= consumed_bytes);

	if (stop_) {
		aos_pool_destroy(p_);
		return;
	}

	auto callback = myup::UploadService::GetInstance()->GetCallback();
	if (nullptr == callback) {
		return;
	}

	int progress = int(consumed_bytes * 100 / (double)total_bytes);
	double now = (double)consumed_bytes;
	double totle = (double)total_bytes;
	if (progress > g_progress) {
		callback->onUploadProgress(id_, progress, now, totle, 0);
		g_progress = progress;
	}

	//if (LOGDEBUG) _cprintf("%%%" APR_INT64_T_FMT "\n", consumed_bytes * 100 / total_bytes);
}

int64_t get_file_size(const char* path) {
	struct _stat64i32 statbuf = { 0 };
	int ret;
	ret = _stat(path, &statbuf);
	return statbuf.st_size;
}

void OSS_Upload::UploadStoppable()
{
	aos_pool_t *p = NULL;
	aos_string_t bucket;
	aos_string_t object;
	int is_cname = 0;
	aos_table_t *headers = NULL;
	aos_table_t *complete_headers = NULL;
	aos_table_t *resp_headers = NULL;
	oss_request_options_t *options = NULL;
	aos_string_t upload_id;
	oss_upload_file_t *upload_file = NULL;
	aos_status_t *s = NULL;
	oss_list_upload_part_params_t *params = NULL;
	aos_list_t complete_part_list;
	oss_list_part_content_t *part_content = NULL;
	oss_complete_part_content_t *complete_part_content = NULL;
	int part_num = 1;
	int64_t pos = 0;
	int64_t file_length = 0;

	aos_pool_create(&p, NULL);
	options = oss_request_options_create(p);
	init_sample_request_options(options, is_cname);
	headers = aos_table_make(p, 1);
	complete_headers = aos_table_make(p, 1);
	aos_str_set(&bucket, BUCKET_NAME);
	aos_str_set(&object, "multipart-key.2");

	std::string remoteobject(TEST_OBJDIR);
	remoteobject.append(paperid_);
	std::string paperpath(local_paper_path_);

	//init mulitipart
	s = oss_init_multipart_upload(options, &bucket, &object,
		&upload_id, headers, &resp_headers);

	if (aos_status_is_ok(s)) {
		printf("Init multipart upload succeeded, upload_id:%.*s\n",
			upload_id.len, upload_id.data);
	}
	else {
		printf("Init multipart upload failed\n");
		aos_pool_destroy(p);
		return;
	}

	std::string file_path = local_paper_path_ + "\\" + src_;

	//upload part from file
	file_length = get_file_size(file_path.c_str());
	while (pos < file_length) {
		upload_file = oss_create_upload_file(p);
		aos_str_set(&upload_file->filename, file_path.c_str());
		upload_file->file_pos = pos;
		pos += 100 * 1024;
		upload_file->file_last = pos < file_length ? pos : file_length; //200k
		s = oss_upload_part_from_file(options, &bucket, &object, &upload_id,
			part_num++, upload_file, &resp_headers);

		if (aos_status_is_ok(s)) {
			printf("Multipart upload part from file succeeded\n");
		}
		else {
			printf("Multipart upload part from file failed\n");
		}

		//// test
		//{
		//	aos_pool_destroy(p);
		//	return;
		//}
	}

	//list part
	params = oss_create_list_upload_part_params(p);
	params->max_ret = 1000;
	aos_list_init(&complete_part_list);
	s = oss_list_upload_part(options, &bucket, &object, &upload_id,
		params, &resp_headers);

	if (aos_status_is_ok(s)) {
		printf("List multipart succeeded\n");
	}
	else {
		printf("List multipart failed\n");
		aos_pool_destroy(p);
		return;
	}

	aos_list_for_each_entry(oss_list_part_content_t, part_content, &params->part_list, node) {
		complete_part_content = oss_create_complete_part_content(p);
		aos_str_set(&complete_part_content->part_number,
			part_content->part_number.data);
		aos_str_set(&complete_part_content->etag, part_content->etag.data);
		aos_list_add_tail(&complete_part_content->node, &complete_part_list);
	}

	//complete multipart
	s = oss_complete_multipart_upload(options, &bucket, &object, &upload_id,
		&complete_part_list, complete_headers, &resp_headers);

	if (aos_status_is_ok(s)) {
		printf("Complete multipart upload from file succeeded, upload_id:%.*s\n",
			upload_id.len, upload_id.data);
	}
	else {
		printf("Complete multipart upload from file failed\n");
	}

	aos_pool_destroy(p);
}

bool OSS_Upload::hasinit_ = false;
